package com.muke.service.impl;/**
 * Created with IntelliJ IDEA.
 * Date: 2019-10-9.
 * Time: 13:25.
 * Explain:
 *
 * @author hezx
 */

import com.muke.dao.SecKillDao;
import com.muke.dao.SuccSecKillDao;
import com.muke.dto.Exposer;
import com.muke.dto.SecKillExecution;
import com.muke.entity.SecKill;
import com.muke.entity.SuccSecKill;
import com.muke.enums.SecKillEnums;
import com.muke.exception.RepeatKillException;
import com.muke.exception.SecKillClosedException;
import com.muke.exception.SecKillException;
import com.muke.service.SecKillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * @ClassName SecKillServiceImpl
 * @Description: TODO
 * @Author hezx
 * @Date 2019-10-9
 * @Version V1.0
 **/
@Service
public class SecKillServiceImpl implements SecKillService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    //MD5 拼接值
    private final String SLAT = "HAJSKHKJajlsdas*a/*-/2a5d5as1da";

    @Resource
    private SecKillDao secKillDao;

    @Resource
    private SuccSecKillDao succSecKillDao;


    @Override
    public List<SecKill> findSecKillList() {
        Integer offset = 0;
        Integer limit = 4;
        return secKillDao.queryAll(offset, limit);
    }

    @Override
    public SecKill getById(Long secKillId) {
        if (secKillId == null)
            throw new RuntimeException();
        return secKillDao.queryById(secKillId);
    }

    @Override
    public Exposer getSecKillUrl(Long secKillId) {

        SecKill secKill = this.getById(secKillId);
        if (secKill == null)
            return new Exposer(false, secKillId);
        //系统当前时间
        Long currentTime = new Date().getTime();
        Long start = secKill.getStartTime().getTime();
        Long end = secKill.getEndTime().getTime();
        //是否在范围之内
        if (currentTime > end || currentTime < start)
            return new Exposer(false, secKillId, currentTime, start, end);

        // 转换特定 md5字符串 过程，不可逆
        String md5 = this.getMd5(secKillId);
        return new Exposer(true, secKillId, md5);
    }

    /*** 
     * @Description: 组合拼接md5
     * @Author: hezx
     * @Date: 2019-10-9
     * @param secKillId
     * @return: java.lang.String
     */
    private String getMd5(Long secKillId) {
        String base = SLAT + "/" + secKillId;
        return DigestUtils.md5DigestAsHex(base.getBytes());
    }


    /***
    * 使用注解控制事务方法的优点:
     * 1. 开发团队达成一致约定，明确标注事务方法的编程风格
     * 2. 保证事务方法的执行时间尽可能短，不要穿插其他网络操作 RPC、HTTP,缓存 请求，或者玻璃到事务方法外部
     * 3. 不是所有的方法都需要事务，如只有一条修改操作，或者 只读操作不需要事务控制
    */
    @Override
    @Transactional
    public SecKillExecution executeSecKill(Long secKillId, Long userPhone, String md5) throws SecKillException, RepeatKillException, SecKillClosedException {
        if (md5 == null || !md5.equals(getMd5(secKillId)))
            throw new SecKillException("seckill data is rewrite");
        try {
            //秒杀逻辑 减库存 + 新增记录
            //减库存
            Date current = new Date();
            int updateCount = secKillDao.reduceNumber(secKillId, current);
            if (updateCount <= 0)
                throw new SecKillClosedException("seckill is close");
            //新增抢购成功记录
            int insertCount = succSecKillDao.insertSuccSecKill(secKillId, userPhone);
            if (insertCount <= 0)
                throw new RepeatKillException("seckill is repeat");
            SuccSecKill succSecKill = succSecKillDao.queryByIdWithSecKill(secKillId, userPhone);
            SecKillExecution secKillExecution = new SecKillExecution(secKillId, SecKillEnums.SUCCESS, succSecKill);
            return secKillExecution;
        } catch (SecKillClosedException e1) {
            throw e1;
        } catch (RepeatKillException e2) {
            throw e2;
        } catch (Exception e) {
            //编译时异常 转换为运行时 异常
            throw new SecKillException("seckill inner error:" + e.getMessage());
        }

    }
}
